package com.ellison.springdemo.utils;

import javassist.*;
import org.junit.Test;

/**
 * <p>TODO</p>
 *
 * @author Ellison_Pei
 * @date 2022/3/21 15:33
 * @since 1.0
 **/
public class JavassistTest {

    /**
     * 类 相关的API
     * 加载类、创建新的类
     */
    @Test
    public void ClassPoolTest() throws NotFoundException, CannotCompileException {
        // 类库, jvm中所加载的class
        ClassPool pool = ClassPool.getDefault();
        // 加载一个已知的类, 注：参数必须为全量类名
        CtClass ctClass = pool.get("com.ellison.springdemo.utils.ApplicationContextUtil");
        // 创建一个新的类, 类名必须为全量类名
        CtClass tClass = pool.makeClass("com.ellison.springdemo.utils.Calculator");


    }

    /**
     * 类中的 Field 属性相关的API
     */
    @Test
    public void CtFieldTest() {
        try {
            // 类库, jvm中所加载的class
            ClassPool pool = ClassPool.getDefault();
            // 加载一个已知的类, 注：参数必须为全量类名
            CtClass ctClass = pool.get("com.ellison.springdemo.utils.ApplicationContextUtil");
            // 获取已知类的属性
            CtField ctField = ctClass.getDeclaredField("name");
            // 构建新的类的成员变量
            CtField ctFieldNew = new CtField(CtClass.intType, "age", ctClass);
            // 设置类的访问修饰符为public
            ctFieldNew.setModifiers(Modifier.PUBLIC);
            // 将属性添加到类中
            ctClass.addField(ctFieldNew);
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }

    }

    /**
     * 类中的 method 相关的API
     */
    @Test
    public void CtMethodTest() {
        try {
            // 类库, jvm中所加载的class
            ClassPool pool = ClassPool.getDefault();
            // 加载一个已知的类, 注：参数必须为全量类名
            CtClass ctClass = pool.get("com.ellison.springdemo.utils.ApplicationContextUtil");
            // 获取已有方法
            //创建新的方法, 参数1:方法的返回类型，参数2：名称，参数3：方法的参数，参数4：方法所属的类
            CtMethod ctMethod = new CtMethod(CtClass.intType, "calc", new CtClass[]
                    {CtClass.intType, CtClass.intType}, ctClass);
            // 设置方法的访问修饰
            ctMethod.setModifiers(Modifier.PUBLIC);
            // 将新建的方法添加到类中
            ctClass.addMethod(ctMethod);
            // 方法体内容代码 $1代表第一个参数，$2代表第二个参数
            ctMethod.setBody("return $1 + $2;");

            // 获取已有的构造方法
            CtMethod ctMethodExisting = ctClass.getDeclaredMethod("sayHello");
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }
    }

    /**
     * 构造方法相关的API
     */
    @Test
    public void CtConstructorTest() {
        try {
            // 类库, jvm中所加载的class
            ClassPool pool = ClassPool.getDefault();
            // 加载一个已知的类, 注：参数必须为全量类名
            CtClass ctClass = pool.get("com.ellison.springdemo.utils.ApplicationContextUtil");
            // 获取已有的构造方法, 参数为构建方法的参数类型数组
            CtConstructor ctConstructorExisting = ctClass.getDeclaredConstructor(new CtClass[]{});

            // 创建新的构造方法
            CtConstructor ctConstructorNoExisting = new CtConstructor(new CtClass[]{CtClass.intType}, ctClass);
            ctConstructorNoExisting.setModifiers(Modifier.PUBLIC);
            ctConstructorNoExisting.setBody("this.age = $1;");
            ctClass.addConstructor(ctConstructorNoExisting);
            // 也可直接创建
            ctConstructorNoExisting = CtNewConstructor.make("public Student(int age){this.age=age;}", ctClass);
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }
    }
}
